// database.cpp
// Customizer_Base
// Protea Customizer/Customizer Pro / Business Logic for managing databases
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// Please send correspondence to:
//
// theproteaproject@gmail.com
//

#include "base_setup.h"

//@ SEC 3
//@ CHA Database Defn Retrieve
//@ COD DatabaseDefnRetrieve
//@ TAB Database_defn Read
//@ GEN Use to retrieve a particular Database_defn record

bool DatabaseDefnRetrieve( XCBuffer* database_defn, XCTransactionVars* vars ) {
	TRANS_INFUNC( DatabaseDefnRetrieve );

	bool result;
	XCDBStatement* stmt = NULL;
	XCSQLGen* query = vars->query;

	try {
		query->MakeFull( SQL_STMT_SELECT, database_defn );
		stmt = vars->dbtxn->ExecuteSQL( query );
		result = (!stmt->Eof());
	} catch( ... ) {
		App->CleanupObjects( 1, &stmt );
		throw;
	};
	App->CleanupObjects( 1, &stmt );

	TRANS_OUTFUNCRET( result );
};


//@ SEC 3
//@ CHA Database Review Tables
//@ COD DatabaseReviewTables
//@ TAB Database_defn Read
//@ GEN Use to retrieve a list of tables in a database

XCDBStatement* DatabaseReviewTables( XCBuffer* dataset_descr, XCTransactionVars* vars ) {
	TRANS_INFUNC( DatabaseReviewTables );

	XCDBStatement* stmt = NULL;
	XCSQLGen* query = vars->query;

	try {
		query->MakeSELECT( dataset_descr );
		query->AddWHERE( 2,
			dataset_descr->GetField( FLD_APPL_NBR ),
			dataset_descr->GetField( FLD_DATABASE_NBR ));
		stmt = vars->dbtxn->ExecuteSQL( query );
	} catch( ... ) {
		App->CleanupObjects( 1, &stmt );
		throw;
	};

	TRANS_OUTFUNCRET( stmt );
};


//@ SEC 3
//@ CHA Database Defn Verify
//@ COD DatabaseDefnVerify
//@ TAB DATABASE_DEFN Read
//@ GEN Use to verify DATABASE_DEFN records

void DatabaseDefnVerify( XCBuffer* database_defn, verify_type verify_mode, XCTransactionVars* vars ) {
	TRANS_INFUNC( DatabaseDefnVerify );

	XCBuffer* dataset_descr = NULL;
	XCDBStatement* stmt = NULL;

	try {
		//@ Force application names to be uppercase
		database_defn->GetField( FLD_DATABASE_NAME )->Uppercase();

		//@ SUB ValidateName to check for a valid name
		ValidateName( FLD_DATABASE_NAME, database_defn, ERR_DATABASE, vars );

		//@ If verifying an ADD transaction,
		if (verify_mode == VERIFY_ADD) {
			//@ GRS

			//@ Verify that a database does not already exist
			//@ SUB LookupAppNumber to convert field name into a field number
			//@ ERR C (ALREADY_EXISTS) Database already exists
			if ( LookupNumberFromName( vars, vars->buffer, database_defn, FLD_DATABASE_NBR, FLD_DATABASE_NAME, FMT_DATABASE_DEFN ) )
				vars->Errors->Add( ERR_ALREADY_EXISTS, SEV_CRITICAL, FLD_DATABASE_NAME, 1, ERR_DATABASE );

			//@ GRE
		} else { // Review, Change or delete
			//@ GRS
			
			//@ Verify that the database does exist, if not:
			//@ ERR E (DOESNT EXIST) Database doesn't exist
			if ( ! LookupNumberFromName( vars, vars->buffer, database_defn, FLD_DATABASE_NBR, FLD_DATABASE_NAME, FMT_DATABASE_DEFN ) ||
				!DatabaseDefnRetrieve( database_defn, vars ))
				vars->Errors->Add( ERR_DOESNT_EXIST, SEV_CRITICAL, FLD_DATABASE_NAME, 1, ERR_DATABASE );

			//@ GRE
		};

		//@ If verifying a DELETE transaction,
		if (verify_mode == VERIFY_DELETE) {
			//@ Ensure that there are no tables currently associated with this database
			dataset_descr = vars->myapp->CreateBuffer( FMT_DATASET_DESCR );
			dataset_descr->MoveCorresponding( database_defn, vars->Errors );
			stmt = DatabaseReviewTables( dataset_descr, vars );
			if (!stmt->Eof())
				vars->Errors->Add( ERR_DATABASE_IN_USE, SEV_CRITICAL, FLD_DATABASE_NAME );
		};
	} catch( ... ) {
		vars->myapp->CleanupObjects( 2, &dataset_descr, &stmt );
		throw;
	};
	vars->myapp->CleanupObjects( 2, &dataset_descr, &stmt );

	TRANS_OUTFUNC();
};


//@ SEC 3
//@ CHA Database Defn Add
//@ COD DatabaseDefnAdd
//@ TAB Database_defn write
//@ GEN Use to add database_defn records

void DatabaseDefnAdd( XCBuffer* database_defn, XCTransactionVars* vars ) {
	XCDBStatement* stmt = NULL;
	XCBuffer* temp = NULL;
	XCSQLGen* query = vars->query;

	long last_number = 0;

	TRANS_INFUNC( DatabaseDefnAdd );

	//@ Determine the next available application number
	temp = App->CreateBuffer( FMT_DATABASE_DEFN );
	try {
		XCOrder ord;
		bool found = false;

		query->MakeSELECT( temp );
		query->AddWHERE( 1, database_defn->GetField( FLD_APPL_NBR ) );
		query->AddORDERBY( FLD_DATABASE_NBR, &ord );
		stmt = vars->dbtxn->ExecuteSQL( query );
		while (!found && !stmt->Eof()) {
			long value = temp->GetField( FLD_DATABASE_NBR )->GetLong( true );
			if (value != last_number + 1) {
				// We found a blank space
				found = true;
			} else {
				last_number = value;
				stmt->MoveNext();
			};
		};

		// Cleanup now
		App->CleanupObjects( 2, &temp, &stmt );

		database_defn->GetField( FLD_DATABASE_NBR )->SetLong( last_number + 1 );
	} catch( ... ) {
		App->CleanupObjects( 2, &temp, &stmt );
		throw;
	};

	//@ Add the database record
	query->MakeFull( SQL_STMT_INSERT, database_defn );
	vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );

	TRANS_OUTFUNC();
};


//@ SEC 3
//@ CHA Database Defn Delete
//@ COD DatabaseDefnDelete
//@ TAB Appl_defn write
//@ GEN Use to delete appl_defn records

void DatabaseDefnDelete( XCBuffer* database_defn, XCTransactionVars* vars ) {
	TRANS_INFUNC( DatabaseDefnDelete );

	XCSQLGen* query = vars->query;

	//@ Delete the application record
	query->MakeFull( SQL_STMT_DELETE, database_defn );
	vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );

	TRANS_OUTFUNC();
};


//@ SEC 3
//@ CHA Database Defn Delete All
//@ COD DatabaseDefnDeleteAll
//@ TAB DATABASE_DEFN Write
//@ GEN Use to delete all DATABASE_DEFN records for an application
void DatabaseDefnDeleteAll( XCBuffer* buf, XCTransactionVars* vars ) 
{
	TRANS_INFUNC( DatabaseDefnDeleteAll );

	XCBuffer* database_defn = NULL;
	XCSQLGen* query = vars->query;

	try {
		//@ Create a DATABASE_DEFN record
		database_defn = App->CreateBuffer( FMT_DATABASE_DEFN, false );

		//@ Move in application number
		database_defn->MoveCorresponding( buf, NULL );

		//@ Delete all DATABASE_DEFN records
		query->MakeDELETE( database_defn );
		query->AddWHERE( 1, database_defn->GetField( FLD_APPL_NBR ) );
		vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );
	} catch( ... ) {
		vars->myapp->CleanupObjects( 1, &database_defn );
		throw;
	};
	vars->myapp->CleanupObjects( 1, &database_defn );

	TRANS_OUTFUNC();
};
